/*
 * Copyright 2011 Vlad Goldman.  All rights reserved.
  
   This file is part of GooOutSync - Google Outlook Sync application
  
    Project page: http://goooutsync.googlecode.com

    GooOutSync is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    GooOutSync is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with GooOutSync.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.vgold.goooutsync;

import java.util.Iterator;
import java.util.Properties;
import java.util.SortedMap;
import java.util.TreeMap;

import org.apache.log4j.Logger;

import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComFailException;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;
import com.vgold.goooutsync.AContact.Fields;

public class OutlookContacts {

	static SortedMap<String, OutlookContact> outlookContactList = new TreeMap<String, OutlookContact>();
	//private static String outlookContactMappingPropertiesFile = "resources/properties/OutlookContactsMapping.properties";
	//private static String googleContactMappingPropertiesFile = "resources/properties/GoogleContactsMapping.properties";
	static Properties outlookMappingProperties;
	static Properties googleMappingProperties;
	static Logger logger = Logger.getLogger(OutlookContacts.class);
	Iterator contactListIterator;
	Dispatch items;
	Dispatch olo;
	private GooOutSyncStatus gooOutSyncStatus;

	public OutlookContacts() throws GooOutSyncException {
		gooOutSyncStatus = GooOutSyncStatus.getGooOutSyncStatus();
		// System.out.println(new java.io.File(".").getAbsolutePath());
		ActiveXComponent axOutlook = new ActiveXComponent("Outlook.Application");
		try {
			logger.debug("version=" + axOutlook.getProperty("Version"));

			//Object oOutlook = axOutlook.getObject();
			// System.out.println("version="+Dispatch.get(oOutlook, "Version"));

			// Object oNameSpace =
			// axOutlook.getProperty("Session").toDispatch();
			// System.out.println("oNameSpace=" + oNameSpace);

			// recurseFolders(0, oNameSpace);
			//Dispatch dsp = new Dispatch();

			olo = (Dispatch) axOutlook.getObject();
			Dispatch myNamespace = Dispatch.call(olo, "GetNamespace", "MAPI")
					.toDispatch();
			Dispatch myFolder = Dispatch.call(myNamespace, "GetDefaultFolder",
					new Integer(MSOutlookConstants.olFolderContacts))
					.toDispatch();

			items = Dispatch.get(myFolder, "Items").toDispatch();

			int count = Dispatch.call(items, "Count").toInt();
			logger.info("Total contacts:" + count);
			for (int i = 1; i <= count; i++) {
				logger.info("Contact item:" + i);
				Dispatch item;
				item = Dispatch.call(items, "Item", new Integer(i))
						.toDispatch();
				// Dispatch itemClass;
				Integer itemClass = Dispatch.get(item, "Class").getInt();
				if (itemClass == MSOutlookConstants.olDistributionList) {
					logger.info("Skipping DL:"
							+ Dispatch.get(item, "DLName").toString());
					continue;
				}
				gooOutSyncStatus.incOutlookContacts();
				OutlookContact ocontact = createOutlookContact(item);
				outlookContactList.put(ocontact.getField(Fields.OUTID),
						ocontact);
			}

		} catch (ComFailException cfe) {
			// "Can't get object clsid from progid" means Word not installed,
			// also get "can't co-create object".
			throw new GooOutSyncException((Throwable) cfe);
		} catch (UnsatisfiedLinkError ule) {
			throw new GooOutSyncException((Throwable) ule);
			// "Can't get object clsid from progid" means Word not installed.

		} finally {
			// axOutlook.invoke("Quit", new Variant[] {});
		}
	}

	/*
	 * private static void recurseFolders(int iIndent, Object o) {
	 * 
	 * if (o == null) return; Object oFolders = Dispatch.get(o,
	 * "Folders").toDispatch(); // System.out.println("oFolders=" + oFolders);
	 * if (oFolders == null) return;
	 * 
	 * Object oFolder = Dispatch.get(oFolders, "GetFirst").toDispatch(); do {
	 * Object oFolderName = Dispatch.get(oFolder, "Name"); if (null ==
	 * oFolderName) { break; }
	 * 
	 * System.out.println(pad(iIndent) + oFolderName); recurseFolders(iIndent +
	 * 3, oFolder);
	 * 
	 * oFolder = Dispatch.get(oFolders, "GetNext").toDispatch(); } while(true);
	 * 
	 * }
	 */

	public Dispatch createContact() throws GooOutSyncException {
		Dispatch contact;
		try {
			logger.info("Starting create Outlook Contact");
			// contact = Dispatch.call(items, "Create", Dispatch.Get, new
			// Object[] { "0" }, new int[0]).toDispatch();
			contact = Dispatch.call(olo, "CreateItem", new Variant(2))
					.toDispatch();
		} catch (ComFailException e) {
			logger.error("Failed create outlook Contact!", e);
			// TODO Auto-generated catch block
			throw new GooOutSyncException(e);
		}
		return contact;
	}

	public void deleteOutlookContact(OutlookContact oContact) {
		oContact.delete();
	}

	public void updateOutlookContact(OutlookContact oContact) {
		oContact.save();
	}

	public static OutlookContact createOutlookContact(Dispatch item)
			throws GooOutSyncException {
		// GenericContactItem
		OutlookContact contact = new OutlookContact(item);
		/*
		 * SortedMap<String,String> contact=new GenericContactItem(); for(String
		 * key : outlookMappingProperties.stringPropertyNames()) { String value
		 * = outlookMappingProperties.getProperty(key); contact.put(key,
		 * Dispatch.get(item, value).toString()); } logger.info("NEXT CONTACT");
		 * /*for(String key : outlookMappingProperties.stringPropertyNames()) {
		 * String value = outlookMappingProperties.getProperty(key);
		 * logger.info(key+" => "+contact.get(key)); }
		 */
		Iterator iterator = contact.keySet().iterator();
		while (iterator.hasNext()) {
			Object key = iterator.next();
			logger.info(key + " => " + contact.get(key));
		}
		logger.info("Modified => " + contact.getModified());
		return contact;
	}

	// private static void initializeProperties (){
	// outlookMappingProperties = new Properties();
	// googleMappingProperties = new Properties();
	// try {
	// outlookMappingProperties.load(Outlook.class.getResourceAsStream(outlookContactMappingPropertiesFile));
	// } catch (IOException e) {
	// logger.fatal("Failed loading Outlook properties file. "+e.getMessage());
	// System.exit(0);
	// }
	// try {
	// googleMappingProperties.load(Outlook.class.getResourceAsStream(googleContactMappingPropertiesFile));
	// } catch (IOException e) {
	// logger.fatal("Failed loading Google properties file. "+e.getMessage());
	// System.exit(0);
	// }
	//
	// }

	public void resetIterator() {
		contactListIterator = outlookContactList.keySet().iterator();
	}

	public OutlookContact getNext() {
		if (contactListIterator.hasNext()) {
			Object key = contactListIterator.next();
			logger.info(key + " => " + outlookContactList.get(key));
			return (outlookContactList.get(key));
		}
		return null;
	}

	public boolean hasNext() {
		return contactListIterator.hasNext();
	}

	public boolean containsKey(String key) {
		return outlookContactList.containsKey(key);
	}

	public OutlookContact get(String key) {
		return outlookContactList.get(key);
	}

	public void processed(String key) {
		outlookContactList.remove(key);
	}

	public OutlookContact createOutlookContact(AContact refContact)
			throws GooOutSyncException {
		Dispatch item = createContact();
		OutlookContact newOutlookContact = new OutlookContact(item);
		newOutlookContact.updateWith(refContact);
		// newContactEntry=createContact(newGoogleContact.getContactEntry());
		newOutlookContact.save();
		newOutlookContact.setField(Fields.OUTID, newOutlookContact.getOutID());
		newOutlookContact.save();
		return newOutlookContact;
	}

	public void cleanGoogleIdFromOutlookContacts() throws GooOutSyncException {

		try {
			int count = Dispatch.call(items, "Count").toInt();
			logger.info("Cleaning Google ID from " + count
					+ " Outlook contacts");
			for (int i = 1; i <= count; i++) {
				if (i == 298) {
					int a = 0;
				}
				logger.info("Contact item:" + i);
				Dispatch item;
				item = Dispatch.call(items, "Item", new Integer(i))
						.toDispatch();
				// Dispatch itemClass;
				Integer itemClass = Dispatch.get(item, "Class").getInt();
				if (itemClass == MSOutlookConstants.olDistributionList) {
					logger.info("Skipping DL:"
							+ Dispatch.get(item, "DLName").toString());
					continue;
				}
				OutlookContact oContact = OutlookContacts
						.createOutlookContact(item);
				if (!oContact.getField(Fields.GOOID).isEmpty()) {
					logger.info("Removing Google ID from "
							+ oContact.toString());
					oContact.updateField(Fields.GOOID, "");
					oContact.save();
				}
			}

		} catch (ComFailException cfe) {
			// "Can't get object clsid from progid" means Word not installed,
			// also get "can't co-create object".
			throw new GooOutSyncException((Throwable) cfe);
		} catch (UnsatisfiedLinkError ule) {
			throw new GooOutSyncException((Throwable) ule);
			// "Can't get object clsid from progid" means Word not installed.

		} finally {
			// axOutlook.invoke("Quit", new Variant[] {});
		}
	}
}
